﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Serialization;

namespace DotNetDal.Core.Data
{
    /// <summary>
    /// A dictionary of all the fields of a table
    /// </summary>
    [Serializable]
    public class FieldList : Dictionary<string, object>, ISerializable, IComparable
    {

        /// <summary>
        /// Construct a new Fieldlist containing the specified fieldnames
        /// </summary>
        /// <param name="fieldNames"></param>
        public FieldList(params string[] fieldNames)
            : base()
        {
            foreach (string name in fieldNames)
                this.Add(name, null);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="keyValues"></param>
        public FieldList(Dictionary<string, object> keyValues)
            : base()
        {
            foreach (string name in keyValues.Keys)
                this.Add(name, keyValues[name]);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="fieldNames"></param>
        public FieldList(IEnumerable<string> fieldNames)
            : base()
        {
            foreach (string name in fieldNames)
                this.Add(name, null);
        }

        #region ISerializable Members
        /// <summary>
        /// Serialization constructor
        /// </summary>
        /// <param name="info"></param>
        /// <param name="ctxt"></param>
        public FieldList(SerializationInfo info, StreamingContext ctxt)
            : base()
        {
            string list = (string)info.GetValue("_fields", typeof(string));
            foreach (string field in list.Split(','))
            {
                if (field != "")
                {
                    Type fieldType = (Type)info.GetValue(field + "_type", typeof(Type));
                    if (info.GetValue(field + "_type", typeof(object)) == null)
                        this.Add(field, null);
                    else if (fieldType == typeof(DateTime))
                        this.Add(field, Convert.ToDateTime((string)info.GetValue(field, typeof(string))));
                    else if (fieldType == typeof(System.Xml.XmlDocument))
                    {
                        System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
                        doc.LoadXml((string)info.GetValue(field, typeof(string)));
                        this.Add(field, doc);
                    }
                    else
                        this.Add(field, info.GetValue(field, ((Type)info.GetValue(field + "_type", typeof(Type)))));
                }
            }

            /*Dictionary<string, object> fl = (Dictionary<string, object>)info.GetValue("_data", typeof(Dictionary<string, object>));
            foreach (string key in fl.Keys)
                this.Add(key, fl[key]);*/
        }
        /// <summary>
        /// Serialization method
        /// </summary>
        /// <param name="info"></param>
        /// <param name="context"></param>
        void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
        {
            string fieldlist = string.Empty;
            foreach (string field in this.Keys)
                fieldlist += field + ",";
            fieldlist = fieldlist.TrimEnd(',');
            info.AddValue("_fields", fieldlist);


            foreach (string field in this.Keys)
                if (this[field] != null)
                {
                    if (this[field] is System.Xml.XmlDocument)
                        info.AddValue(field, ((System.Xml.XmlDocument)this[field]).OuterXml);
                    else if (this[field] is DateTime)
                        info.AddValue(field, ((DateTime)this[field]).ToString("s"));
                    else
                        info.AddValue(field, this[field].ToString());

                    info.AddValue(field + "_type", this[field].GetType());
                }
                else
                {
                    info.AddValue(field, null);
                    info.AddValue(field + "_type", null);
                }
        }

        #endregion



        #region IComparable Members
        #region Helpers
        /// <summary>
        /// is one equal to two?
        /// </summary>
        /// <param name="one"></param>
        /// <param name="two"></param>
        /// <returns></returns>
        protected bool _areEqual(object one, object two)
        {
            if (one is IComparable)
            {
                return ((IComparable)one).CompareTo(two) == 0;
            }
            else
                return one == two;
        }
        #endregion
        /// <summary>
        /// 
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public int CompareTo(object obj)
        {
            if (obj is FieldList)
            {
                FieldList other = obj as FieldList;
                foreach (string field in this.Keys)
                    if (other.ContainsKey(field))
                    {
                        if (!_areEqual(this[field], other[field]))
                            return 1;
                    }
                    else
                        return 2;

                foreach (string field in other.Keys)
                    if (this.ContainsKey(field))
                    {
                        if (!_areEqual(this[field], other[field]))
                            return 1;
                    }
                    else
                        return 2;

                return 0;
            }
            else
                return -1;
        }

        #endregion
    }
}
